package com.bjsxt.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.enums.ApiErrorCode;
import com.baomidou.mybatisplus.extension.exceptions.ApiException;
import com.bjsxt.domain.SysMenu;
import com.bjsxt.feign.JwtToken;
import com.bjsxt.feign.OAuth2FeignClient;
import com.bjsxt.model.LoginResult;
import com.bjsxt.service.SysLoginService;
import com.bjsxt.service.SysMenuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * description:
 * 登录的接口实现类
 *
 * @author ALIENWARE <br />
 * @date 2021/6/24
 */
@Service
@Slf4j
public class SysLoginServiceImpl implements SysLoginService {

    /**
     * authorization-server openFegin 远程调用服务接口
     */
    @Autowired
    private OAuth2FeignClient oAuth2FeignClient;

    /**
     * 菜单业务层接口
     */
    @Autowired
    private SysMenuService sysMenuService;

    /**
     * redis 客户端
     */
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 就是 Auth2 授权生产的第三方加密的值 账户: coin-api 密码: coin-secret 生成的加密值
     */
    @Value("${basic.token:Basic Y29pbi1hcGk6Y29pbi1zZWNyZXQ=}")
    private String basicToken;

    /**
     * 登录方法
     * 1. 获取 token
     * 2. 获取用户登录的菜单
     * 3. 获取用户的权限
     *
     * @param userName 用户名
     * @param password 密码
     * @return 登录返回值对象
     */
    @Override
    public LoginResult login(String userName, String password) {
        // log.info("用户{}开始登录", userName);
        // 获取 token 需要远程调用 authorization-server 服务进行 token 的获取
        ResponseEntity<JwtToken> tokenResponseEntity = oAuth2FeignClient.getToken("password", userName, password, "admin_type", basicToken);
        // 如果获取的数据没有成功抛出异常
        if (tokenResponseEntity.getStatusCode() != HttpStatus.OK)
            throw new ApiException(ApiErrorCode.FAILED);
        // 正常取出返回的对象
        JwtToken jwtToken = tokenResponseEntity.getBody();
        // 打印日志
        // log.info("远程调用授权服务器成功,获取的 token 为{}", JSON.toJSONString(jwtToken, true));
        // 取出我们的 token 值
        String token = jwtToken.getAccessToken();
        // 解析 token 值
        Jwt jwt = JwtHelper.decode(token);
        // 将解析的 jwt 对象值转换为 json token 值
        String jwtJsonStr = jwt.getClaims();
        // 将 json 字符串转换为 json 对象
        JSONObject jwtJson = JSON.parseObject(jwtJsonStr);
        // 获取 json 对象里面的 user_name 的值(也就是 user_id)
        Long userId = Long.valueOf(jwtJson.getString("user_name"));
        // 调用业务层代码使用用户的 id 查询用户的菜单
        List<SysMenu> menus = sysMenuService.getMenusByUserId(userId);
        // 获取用户的权限数据 token 里面有直接取出
        JSONArray authoritiesJsonArray = jwtJson.getJSONArray("authorities");
        // 进行数据对象的转换
        List<SimpleGrantedAuthority> authorities = authoritiesJsonArray.stream().map(authorityJson -> new SimpleGrantedAuthority(authorityJson.toString())).collect(Collectors.toList());
        // 将 token 数据存储在 redis 里面, 否则网关验证的时候是进入不到对应的服务的
        redisTemplate.opsForValue().set(token, "", jwtToken.getExpiresIn(), TimeUnit.SECONDS);
        // 返回 token 数据
        return new LoginResult(jwtToken.getTokenType() + ' ' + token, menus, authorities);
    }
}
